<?php

namespace ASW\Utility;

use JetBrains\PhpStorm\Pure;

class Ini
{
    private string $_content = '';
    private array  $_data    = [];
    private bool   $_parsed  = false;

    public function __construct(string $content = '')
    {
        $this->_content = $content;
        $this->parse();
    }

    private function parse()
    {
        if ($this->_parsed) return;

        $lines = explode("\n", $this->_content);  //按换行拆开,放到数组中， 依次处理每一行

        $sectionRegex  = '/^\[([^\s]*)]$/';
        $keyValueRegex = '/^([^\s])\s*=\s*([^\s]*)$/';

        $_curSection  = '';
        $_curKey      = '';
        $_curComments = [];

        foreach ($lines as $line) {
            $text = trim($line);
            if ($text === '') continue;

            if (str_starts_with($text, "#") || str_starts_with($text, "'") || str_starts_with($text, ";") || str_starts_with($text, '//')) {
                $_curComments[] = $text;
                continue;
            }

            $matches = [];
            if (preg_match($sectionRegex, $text, $matches) > 0)   // 本行是否是 node
            {
                $section               = $matches[1];
                $this->_data[$section] = [
                    'remarks' => $_curComments,
                    'items'   => [],
                ];
                $_curSection           = $section;
                $_curComments          = '';
                continue;
            }

            $matches = [];
            if (preg_match($keyValueRegex, $text, $matches) > 0)   // 本行是否是 item
            {
                $itemKey = trim($matches[1]);
                $itemVal = trim($matches[2]);

                $matches = [];
                // 去除键的前后引号
                if (preg_match('/^\"(.*)\"$/', $itemKey, $matches) > 0) $itemKey = $matches[1];

                $_curKey                                      = $itemKey;
                $this->_data[$_curSection]['items'][$_curKey] = [
                    'remarks' => $_curComments,
                    'value'   => $itemVal,
                ];
                $_curComments                                 = '';
            } else {
                // 啥都不是， 那就是前一个item的值换行了
                if (!empty($this->_curSection) && !empty($_curKey)) {
                    $this->_data[$this->_curSection]['items'][$_curKey]['value'] .= "\n$line";    // 用未trim之前的 $line, 不用 $text
                }
            }
        }

        $this->_parsed = true;
    }

    public static function fromFile($fileName, $encoding = 'GB2312'): ?Ini
    {
        if (empty($fileName)) return null;

        // file_exists 的bug， 路径中若存在中文则不识别， 必须先转码
        $fileName = iconv('UTF-8', 'GBK', $fileName);
        if (!file_exists($fileName)) return null;

        // 临时文件， 如果临时文件不存在则从原始文件拷贝
        // 如果临时文件存在， 则直接读取临时文件
        $tempFileName = "$fileName.temp";
        if (!file_exists($tempFileName)) copy($fileName, $tempFileName);

        $content = file_get_contents($tempFileName);
        if (!empty($encoding)) $content = iconv($encoding, 'UTF-8', $content);

        return self::fromString($content);
    }

    public static function fromString($content): Ini
    {
        return new Ini($content);
    }

    public function getSections(): array
    {
        return array_keys($this->_data);
    }

    #[Pure]
    public function getKeys($sectionName): array
    {
        if (!$this->sectionExists($sectionName)) return [];

        return array_keys($this->_data[$sectionName]['items']);
    }

    public function sectionExists($sectionName): bool
    {
        return array_key_exists($sectionName, $this->_data);
    }

    #[Pure]
    public function getValue($section, $key, $default = null)
    {
        if (!$this->sectionExists($section)) return $default;
        if (!$this->keyExists($section, $key)) return $default;

        return $this->_data[$section]['items'][$key]['value'];
    }

    #[Pure]
    public function keyExists($sectionName, $keyName): bool
    {
        if (!$this->sectionExists($sectionName)) return false;

        return array_key_exists($keyName, $this->_data[$sectionName]['items']);
    }

    /* ================  写   ==================*/

    public function setValue($section, $key, $value): bool
    {
        if (false === $this->addSection($section)) return false;
        if (empty($key)) return false;

        $this->_data[$section]['items'][$key] = [
            'remarks' => '',
            'value'   => strval($value),
        ];

        return true;
    }

    public function addSection($section): bool
    {
        if (empty($section)) return false;
        if ($this->sectionExists($section)) return true;
        $this->_data[$section] = [
            'remarks' => '',
            'items'   => [],
        ];

        return true;
    }

    public function removeKey($section, $key): bool
    {
        if (!$this->sectionExists($section)) return false;
        unset($this->_data[$section]['items'][$key]);

        return !array_key_exists($section, $this->_data[$section]['items']);
    }

    public function removeSection($section): bool
    {
        if (empty($section)) return false;
        unset($this->_data[$section]);

        return !array_key_exists($section, $this->_data);
    }

    public function save($fileName, $encoding = 'GB2312'): bool
    {
        if (false === $this->saveTempFile($fileName, $encoding)) return false;

        // file_exists 的bug， 路径中若存在中文则不识别， 必须先转码
        $fileName     = iconv('UTF-8', 'GBK', $fileName);
        $tempFileName = "$fileName.temp";

        return copy($tempFileName, $fileName);
    }

    public function saveTempFile($fileName, $encoding = 'GB2312'): bool
    {
        if (empty($fileName)) return false;
        // file_exists 的bug， 路径中若存在中文则不识别， 必须先转码
        $fileName     = iconv('UTF-8', 'GBK', $fileName);
        $tempFileName = "$fileName.temp";
        $content      = $this->getContent();
        if (!empty($encoding)) $text = iconv('UTF-8', $encoding, $content);

        return file_put_contents($tempFileName, $content) === strlen($content);
    }

    #[Pure]
    public function getContent(): string
    {
        $content = '';
        foreach ($this->_data as $sectionName => $sectionObject) {
            $remarkText = $this->getRemarkText($sectionObject);
            if (!empty($remarkText)) $content .= "$remarkText\r\n";
            $content .= "[$sectionName]\r\n";

            foreach ($sectionObject['items'] as $keyName => $keyObject) {
                $remarkText = $this->getRemarkText($keyObject);
                if (!empty($remarkText)) $content .= "$remarkText\r\n";

                $value   = $keyObject['value'];
                $content .= "$keyName=$value\r\n";
            }
        }

        return trim($content);
    }

    private function getRemarkText($obj): string
    {
        if (!is_array($obj) || !is_array($obj['remarks'])) return '';

        return implode("\r\n", $obj['remarks']);
    }

    public function dropTempFile($fileName)
    {
        if (empty($fileName)) return;
        // file_exists 的bug， 路径中若存在中文则不识别， 必须先转码
        $fileName     = iconv('UTF-8', 'GBK', $fileName);
        $tempFileName = "$fileName.temp";

        unlink($tempFileName);
    }
}
